package com.zx.onlineeducation.common.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * Jwt 工具类
 *
 * @author 郑雪
 * @since 2022-01-25
 */
public class JwtUtils {

    // 加密算法
    private final static SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;

    // 私钥 / 生成签名的时候使用的秘钥secret，一般可以从本地配置文件中读取，切记这个秘钥不能外露，只在服务端使用，在任何场景都不应该流露出去。
    // 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
    private final static String SECRET = "5Zyo57q/5pWZ6IKy";

    // 过期时间（单位秒）/ 2小时
    private final static Long ACCESS_TOKEN_EXPIRATION = 7200L;

    // jwt签发者
    private final static String JWT_ISS = "xuezheng";

    // jwt所有人
    private final static String SUBJECT = "guli-user";

    /**
     * 生成token字符串的方法
     *
     * @param id 会员id
     * @param nickName 会员昵称
     * @return 返回生成的jwt token
     * @author 郑雪
     * @since 2022-01-25
     */
    public static String getJwtToken(String id, String nickName){
        // 头部 map / Jwt的头部承载，第一部分
        // 可不设置 默认格式是{"alg":"HS256"}
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        //载荷 map / Jwt的载荷，第二部分
        Map<String,Object> claims = new HashMap<>();

        //私有声明 / 自定义数据，根据业务需要添加
        claims.put("id", id);
        claims.put("nickName", nickName);

        //标准中注册的声明 (建议但不强制使用)
        //一旦写标准声明赋值之后，就会覆盖了那些标准的声明
        claims.put("iss", JWT_ISS);
            /*	iss: jwt签发者
                sub: jwt所面向的用户
                aud: 接收jwt的一方
                exp: jwt的过期时间，这个过期时间必须要大于签发时间
                nbf: 定义在什么时间之前，该jwt都是不可用的.
                iat: jwt的签发时间
                jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击
            */

        // 当前时间
        Date currentDate = new Date();

        // 过期时间
        Date expireDate = new Date(currentDate.getTime() + ACCESS_TOKEN_EXPIRATION * 1000);

        //下面就是在为payload添加各种标准声明和私有声明了
        return Jwts.builder()                       // 这里其实就是new一个JwtBuilder，设置jwt的body
            .setHeader(map)                         // 头部信息
            .setClaims(claims)                      // 载荷信息
            .setId(UUID.randomUUID().toString())    // 设置jti(JWT ID)：是JWT的唯一标识，从而回避重放攻击。
            .setIssuedAt(currentDate)               // 设置iat: jwt的签发时间
            .setExpiration(expireDate)              // 设置exp：jwt过期时间
            .setSubject(SUBJECT)                    // 设置sub：代表这个jwt所面向的用户，所有人
            .signWith(SIGNATURE_ALGORITHM, SECRET)  // 设置签名：通过签名算法和秘钥生成签名
            .compact();                             // 开始压缩为xxxxx.yyyyy.zzzzz 格式的jwt token
    }

    /**
     *  从jwt中获取 载荷 信息
     * @param jwtToken jwt生成的token字符串
     * @return 载荷信息 {@link Claims}
     */
    private static Claims getClaimsFromJwt(String jwtToken) {
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(jwtToken).getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken jwt生成的token字符串
     * @return 有效true，无效false
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request 请求对象
     * @return 有效true，无效false
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token字符串获取会员id
     * @param request 请求对象
     * @return 会员ID
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Claims claims = JwtUtils.getClaimsFromJwt(jwtToken);
        return (String)claims.get("id");
    }
}
